cmake_minimum_required(VERSION 3.10)
project(armips)

include_directories(${CMAKE_SOURCE_DIR}/app/src/main/cpp)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)

option(ARMIPS_PRECOMPILE_HEADERS "Use precompiled headers" OFF)
option(ARMIPS_REGEXP "Enable regexp expression functions" ON)
option(ARMIPS_USE_STD_FILESYSTEM "Use std::filesystem instead of third party implementation" OFF)
option(ARMIPS_LIBRARY_ONLY "Build libarmips, skip building armips and armipstests binaries" ON)

macro(check_compiler VARIABLE NAME)
	if (${CMAKE_CXX_COMPILER_ID} MATCHES ${NAME})
		set(${VARIABLE} TRUE)
	else()
		set(${VARIABLE} FALSE)
	endif()
endmacro()

check_compiler(CLANG "Clang")
check_compiler(GCC "GNU")

# Helper function to set common parameters for a target
function(init_target TARGET_NAME)
	set_target_properties(${TARGET_NAME} PROPERTIES
		CXX_STANDARD 17
		CXX_STANDARD_REQUIRED TRUE
		CXX_EXTENSIONS FALSE)

	target_include_directories(${TARGET_NAME} BEFORE PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

	if (GCC OR CLANG)
		# clang-cl seems to treat -Wall as -Weverything
		if (NOT MSVC)
			target_compile_options(${TARGET_NAME} PRIVATE -Wall)
		endif()

		target_compile_options(${TARGET_NAME} PRIVATE -Wno-unused-parameter)

		find_package(Threads REQUIRED)
		target_link_libraries(${TARGET_NAME} PRIVATE Threads::Threads)
	endif ()

	if (ARMIPS_REGEXP)
		target_compile_definitions(${TARGET_NAME} PRIVATE ARMIPS_REGEXP=1)
	endif()
endfunction()

file(GLOB SRC_FILES
		ArmipsDroid.cpp

		Archs/Architecture.h
		Archs/Architecture.cpp

		Archs/ARM/Arm.cpp
		Archs/ARM/Arm.h
		Archs/ARM/ArmExpressionFunctions.cpp
		Archs/ARM/ArmExpressionFunctions.h
		Archs/ARM/ArmElfRelocator.cpp
		Archs/ARM/ArmElfRelocator.h
		Archs/ARM/ArmOpcodes.cpp
		Archs/ARM/ArmOpcodes.h
		Archs/ARM/ArmParser.cpp
		Archs/ARM/ArmParser.h
		Archs/ARM/CArmInstruction.cpp
		Archs/ARM/CArmInstruction.h
		Archs/ARM/CThumbInstruction.cpp
		Archs/ARM/CThumbInstruction.h
		Archs/ARM/Pool.cpp
		Archs/ARM/Pool.h
		Archs/ARM/ThumbOpcodes.cpp
		Archs/ARM/ThumbOpcodes.h

		Archs/MIPS/CMipsInstruction.cpp
		Archs/MIPS/CMipsInstruction.h
		Archs/MIPS/Mips.cpp
		Archs/MIPS/Mips.h
		Archs/MIPS/MipsElfFile.cpp
		Archs/MIPS/MipsElfFile.h
		Archs/MIPS/MipsExpressionFunctions.cpp
		Archs/MIPS/MipsExpressionFunctions.h
		Archs/MIPS/MipsElfRelocator.cpp
		Archs/MIPS/MipsElfRelocator.h
		Archs/MIPS/MipsMacros.cpp
		Archs/MIPS/MipsMacros.h
		Archs/MIPS/MipsOpcodes.cpp
		Archs/MIPS/MipsOpcodes.h
		Archs/MIPS/MipsParser.cpp
		Archs/MIPS/MipsParser.h
		Archs/MIPS/PsxRelocator.cpp
		Archs/MIPS/PsxRelocator.h

		Archs/SuperH/SuperH.cpp
		Archs/SuperH/SuperH.h
		Archs/SuperH/CShInstruction.cpp
		Archs/SuperH/CShInstruction.h
		Archs/SuperH/ShParser.cpp
		Archs/SuperH/ShParser.h
		Archs/SuperH/ShOpcodes.cpp
		Archs/SuperH/ShOpcodes.h
		Archs/SuperH/ShElfRelocator.cpp
		Archs/SuperH/ShElfRelocator.h
		Archs/SuperH/ShExpressionFunctions.cpp
		Archs/SuperH/ShExpressionFunctions.h

		Commands/CAssemblerCommand.cpp
		Commands/CAssemblerCommand.h
		Commands/CAssemblerLabel.cpp
		Commands/CAssemblerLabel.h
		Commands/CDirectiveArea.cpp
		Commands/CDirectiveArea.h
		Commands/CDirectiveConditional.cpp
		Commands/CDirectiveConditional.h
		Commands/CDirectiveData.cpp
		Commands/CDirectiveData.h
		Commands/CDirectiveFile.cpp
		Commands/CDirectiveFile.h
		Commands/CDirectiveMessage.cpp
		Commands/CDirectiveMessage.h
		Commands/CommandSequence.cpp
		Commands/CommandSequence.h

		Core/ELF/ElfTypes.h
		Core/ELF/ElfRelocator.cpp
		Core/ELF/ElfRelocator.h
		Core/ELF/ElfFile.cpp
		Core/ELF/ElfFile.h
		Core/Allocations.cpp
		Core/Allocations.h
		Core/Assembler.cpp
		Core/Assembler.h
		Core/Common.cpp
		Core/Common.h
		Core/Expression.cpp
		Core/Expression.h
		Core/ExpressionFunctionHandler.cpp
		Core/ExpressionFunctionHandler.h
		Core/ExpressionFunctions.cpp
		Core/ExpressionFunctions.h
		Core/FileManager.cpp
		Core/FileManager.h
		Core/Misc.cpp
		Core/Misc.h
		Core/SymbolData.cpp
		Core/SymbolData.h
		Core/SymbolTable.cpp
		Core/SymbolTable.h
		Core/Types.cpp
		Core/Types.h

		Parser/DirectivesParser.cpp
		Parser/DirectivesParser.h
		Parser/ExpressionParser.cpp
		Parser/ExpressionParser.h
		Parser/Parser.cpp
		Parser/Parser.h
		Parser/Tokenizer.cpp
		Parser/Tokenizer.h

		Util/ByteArray.cpp
		Util/ByteArray.h
		Util/CRC.cpp
		Util/CRC.h
		Util/EncodingTable.cpp
		Util/EncodingTable.h
		Util/FileClasses.cpp
		Util/FileClasses.h
		Util/FileSystem.cpp
		Util/FileSystem.h
		Util/Util.cpp
		Util/Util.h
)

# Helper function to automatically set source groups for all source files
function(armips_target_sources TARGET SCOPE)
	target_sources("${TARGET}" "${SCOPE}" ${ARGN})
	source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${ARGN})
endfunction()

find_library(log-lib log)

# tinyformat
add_library(tinyformat INTERFACE)
target_include_directories(tinyformat INTERFACE ext/tinyformat)

# libarmips
add_library(armips SHARED ${SRC_FILES})
init_target(armips)
target_link_libraries(armips PUBLIC tinyformat ${log-lib})

if (ARMIPS_USE_STD_FILESYSTEM)
	target_compile_definitions(armips PUBLIC ARMIPS_USE_STD_FILESYSTEM)
else()
	add_subdirectory(ext/filesystem)
	target_link_libraries(armips PUBLIC ghc_filesystem)
endif()

if (WIN32)
	target_link_libraries(armips PUBLIC shlwapi)
endif()

armips_target_sources(armips PRIVATE
	${SRC_FILES}
)

if (ARMIPS_PRECOMPILE_HEADERS)
	target_precompile_headers(armips PRIVATE
		[["Util/FileSystem.h"]]
		<tinyformat.h>
	)
	set_source_files_properties(Util/FileSystem.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
endif()

if(NOT ARMIPS_LIBRARY_ONLY)
	# armips-bin
	add_executable(armips-bin "")
	init_target(armips-bin)
	set_target_properties(armips-bin PROPERTIES OUTPUT_NAME armips)
	target_link_libraries(armips-bin PRIVATE armips)

	armips_target_sources(armips-bin PRIVATE
		Main/CommandLineInterface.cpp
		Main/CommandLineInterface.h
		Main/main.cpp
		Main/Tests.cpp
		Main/Tests.h
	)

	# tests
	add_executable(armipstests "")
	init_target(armipstests)
	target_compile_definitions(armipstests PUBLIC ARMIPS_TESTS)
	target_link_libraries(armipstests PRIVATE armips)

	armips_target_sources(armipstests PRIVATE
		Main/CommandLineInterface.cpp
		Main/CommandLineInterface.h
		Main/main.cpp
		Main/Tests.cpp
		Main/Tests.h
	)

	enable_testing()
	add_test(NAME armipstests COMMAND armipstests ${CMAKE_CURRENT_SOURCE_DIR}/Tests)

	# install
	install(TARGETS armips-bin RUNTIME DESTINATION .)
	install(FILES Readme.md DESTINATION .)
endif()
